//---------------------------------------------------------------------
// Author: jachymko
//
// Description: Base class for all tab expansion caches. Initializes the
//              implementations on a background thread.
//
// Creation Date: Jan 8, 2007
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Threading;

namespace BGShell.PowerShell.TabExpansion
{
    internal abstract class TabExpansionCache
    {
        internal abstract void OnInitialize();

        private readonly Object _syncRoot = new Object();
        private readonly Thread _thread;

        private volatile bool _stop;

        protected TabExpansionCache()
        {
            _thread = new Thread(InvokeOnInitialize)
            {
                IsBackground = true,
                Priority = ThreadPriority.Lowest,
                Name = GetType().Name + ".Initialize",
            };
        }

        public void AbortInitialization()
        {
            _stop = true;

            if (_thread.IsAlive)
            {
                _thread.Join();
            }
        }

        public void EnsureInitializedAsync()
        {
            AppDomain.CurrentDomain.ProcessExit += OnDomainUnload;
            AppDomain.CurrentDomain.DomainUnload += OnDomainUnload;

            Initialize(false);
        }

        public void EnsureInitialized()
        {
            Initialize(true);
        }

        protected bool Stopped
        {
            get { return _stop; }
        }

        private void Initialize(bool wait)
        {
            if (_stop)
            {
                return;
            }

            lock (_syncRoot)
            {
                if ((_thread.ThreadState & ThreadState.Unstarted) != 0)
                {
                    _thread.Start();
                }
            }

            if (wait)
            {
                _thread.Join();
            }
        }

        private void InvokeOnInitialize()
        {
            try
            {
                OnInitialize();
            }
            finally
            {
                AppDomain.CurrentDomain.ProcessExit -= OnDomainUnload;
                AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
            }
        }

        private void OnDomainUnload(Object sender, EventArgs e)
        {
            AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload;
            AppDomain.CurrentDomain.ProcessExit -= OnDomainUnload;

            AbortInitialization();
        }
    }
}
